package sg.edu.ntu.sci.blackboard.agilix;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import sg.edu.ntu.sci.blackboard.agilix.model.Announcement;
import sg.edu.ntu.sci.blackboard.agilix.model.Course;
import sg.edu.ntu.sci.blackboard.agilix.model.CourseDocument;
import sg.edu.ntu.sci.blackboard.agilix.model.CourseElement;
import sg.edu.ntu.sci.blackboard.agilix.model.DiscussionBoard;
import sg.edu.ntu.sci.blackboard.agilix.model.Doc;
import sg.edu.ntu.sci.blackboard.agilix.model.Folder;
import sg.edu.ntu.sci.blackboard.agilix.model.Forum;
import sg.edu.ntu.sci.blackboard.agilix.model.Message;
import sg.edu.ntu.sci.blackboard.agilix.model.MessageAttachment;
import sg.edu.ntu.sci.blackboard.agilix.util.IOUtil;

/**
 *
 * @author ngocgiang
 */
public class AgilixManifestReader {

    private String location; // location to manifest.xml
    private static Log log = LogFactory.getLog(AgilixManifestReader.class);

    public static final String COURSE_TAG = "course";
    public static final String FOLDER_TAG = "folder";
    public static final String ANNOUNCEMENTS_TYPE = "Announcements";
    public static final String DISCUSSION_BOARD_TYPE = "Discussion Board";
    public static final String COURSE_DOCUMENTS_TYPE = "Course Documents";
    public static final String DOCUMENT_TAG = "document";
    public static final String BOARD_TAG = "board";
    public static final String FORUM_TAG = "forum";
    public static final String MESSAGE_TAG = "message";
    public static final String MESSAGE_ATTACHMENT_TAG = "messageattachment";

    public AgilixManifestReader() {
        location = AgilixSettings.getManifest();
    }

    public AgilixManifestReader(String location) {
        this.location = location;
    }

    public List<Course> parseManifest() {
        List<Course> courses = new ArrayList<Course>();

        Document doc = IOUtil.parseXmlFile(location, false);
        if (doc == null) {
            log.error("Cannot parse manifest file");
            return courses;
        }

        Element root = doc.getDocumentElement();
        List<Element> courseElements = getChildElements(COURSE_TAG, root);
        for (Element courseElement : courseElements) {
            Course course = new Course();
            extractCourse(courseElement, course);
            courses.add(course);
        }

        return courses;
    }    

    private static List<Element> getChildElements(String nodeName, Element node) {
        List<Element> children = new ArrayList<Element>();

        NodeList list = node.getChildNodes();
        for (int i=0; i<list.getLength(); i++) {
            if (list.item(i) instanceof Element) {
                Element candidate = (Element)list.item(i);
                if (candidate.getTagName().equals(nodeName))
                children.add(candidate);
            }
        }

        return children;
    }

    private static void extractCourseElement(Element element, CourseElement courseElement) {
        if (element.hasAttribute("id"))
            courseElement.setId(element.getAttribute("id"));
        if (element.hasAttribute("title"))
            courseElement.setTitle(element.getAttribute("title"));
        if (element.hasAttribute("folder"))
            courseElement.setFolder(element.getAttribute("folder"));
        if (element.hasAttribute("file"))
            courseElement.setFile(element.getAttribute("file"));
        if (element.hasAttribute("titlecolor"))
            courseElement.setTitlecolor(element.getAttribute("titlecolor"));
    }

    private static void extractCourse(Element element, Course course) {
        extractCourseElement(element, course);
        if (element.hasAttribute("nativeid"))
            course.setNativeid(element.getAttribute("nativeid"));
        if (element.hasAttribute("state"))
            course.setState(element.getAttribute("state"));
        if (element.hasAttribute("connectionid"))
            course.setConnectionid(element.getAttribute("connectionid"));
        if (element.hasAttribute("website"))
            course.setWebsite(element.getAttribute("website"));
        if (element.hasAttribute("color"))
            course.setColor("color");
        List<Element> folderElements = getChildElements(FOLDER_TAG, element);
        for (Element folderElement : folderElements) {
            Folder folder = extractFolder(folderElement);
            course.getFolders().add(folder);
        }
    }

    private static Folder extractFolder(Element element) {
        if (element.getAttribute("title").equals(ANNOUNCEMENTS_TYPE)) {
            Announcement folder = new Announcement();
            if (element.hasAttribute("subtype"))
                folder.setSubtype(element.getAttribute("subtype"));
            if (element.hasAttribute("permanent"))
                folder.setPermanent(element.getAttribute("permanent"));
            if (element.hasAttribute("restrictiostartndate"))
                folder.setRestrictionstartdate(element.getAttribute("restrictiostartndate"));
            // TODO: extract announcements
            return folder;
        }
        else if (element.getAttribute("title").equals(DISCUSSION_BOARD_TYPE)) {
            DiscussionBoard folder = new DiscussionBoard();
            if (element.hasAttribute("subtype"))
                folder.setSubtype(element.getAttribute("subtype"));
            List<Element> boardElements = getChildElements(BOARD_TAG, element);
            if (boardElements.size() != 1) {
                log.error("Unsupported: More than one board in a discusson board");
                return null;
            }
            List<Element> forumElements = getChildElements(FORUM_TAG, boardElements.get(0));
            for (Element forumElement :  forumElements) {
                Forum forum = new Forum();
                extractForum(forumElement, forum);
                folder.getForums().add(forum);
            }
            return folder;
        }
        else if (element.getAttribute("title").equals(COURSE_DOCUMENTS_TYPE)) {            
            CourseDocument courseDocument = new CourseDocument();
            List<Element> docElements = getChildElements(DOCUMENT_TAG, element);
            for (Element docElement : docElements) {
                Doc doc = new Doc();
                extractDocument(docElement, doc);
                courseDocument.getDocuments().add(doc);
            }
            return courseDocument;
        }
        
        return null;
    }

    private static void extractDocument(Element element, Doc document) {
        extractCourseElement(element, document);
    }

    private static void extractForum(Element element, Forum forum) {
        extractCourseElement(element, forum);
        if (element.hasAttribute("allowanonymous"))
            forum.setAllowanonymous(element.getAttribute("allowanonymous"));
        if (element.hasAttribute("allowattachments"))
            forum.setAllowattachments(element.getAttribute("allowattachments"));
        if (element.hasAttribute("allowcreate"))
            forum.setAllowcreate(element.getAttribute("allowcreate"));
        if (element.hasAttribute("allowdelete"))
            forum.setAllowdelete(element.getAttribute("allowdelete"));
        if (element.hasAttribute("allowedit"))
            forum.setAllowedit(element.getAttribute("allowedit"));
        if (element.hasAttribute("allowmemberrate"))
            forum.setAllowmemberrate(element.getAttribute("allowmemberrate"));
        if (element.hasAttribute("allowsubscribe"))
            forum.setAllowsubscribe(element.getAttribute("allowsubscribe"));
        List<Element> childElements = getChildElements(MESSAGE_TAG, element);
        for (Element childElement : childElements) {
            Message child = new Message();
            extractMessage(childElement, child);
            forum.getMessages().add(child);
        }
    }

    private static void extractMessage(Element element, Message message) {
        extractCourseElement(element, message);
        if (element.hasAttribute("own"))
            message.setOwn(element.getAttribute("own"));
        if (element.hasAttribute("author"))
            message.setAuthor(element.getAttribute("author"));
        if (element.hasAttribute("locked"))
            message.setLocked(element.getAttribute("lock"));
        if (element.hasAttribute("attachment"))
            message.setAttachment(element.getAttribute("attachment"));
        if (element.hasAttribute("totalviews"))
            message.setTotalviews(element.getAttribute("totalviews"));
        if (element.hasAttribute("read"))
            message.setRead(element.getAttribute("read"));
        if (element.hasAttribute("postdate"))
            message.setPostdate(element.getAttribute("postdate"));
        if (element.hasAttribute("myviews"))
            message.setMyviews(element.getAttribute("myviews"));
        List<Element> childElements = getChildElements(MESSAGE_TAG, element);
        for (Element childElement : childElements) {
            Message child = new Message();
            extractMessage(childElement, child);
            message.getChildren().add(child);
        }
        List<Element> childAttachmentElements = getChildElements(MESSAGE_ATTACHMENT_TAG, element);
        for (Element childAttachmentElement : childAttachmentElements) {
            MessageAttachment childAttachment = new MessageAttachment();
            extractMessageAttachment(childAttachmentElement, childAttachment);
            message.getAttachments().add(childAttachment);
        }
    }

    private static void extractMessageAttachment(Element element, MessageAttachment attachment) {
        extractCourseElement(element, attachment);
        if (element.hasAttribute("filesize"))
            attachment.setFilesize(element.getAttribute("filesize"));
    }

    public static void main(String[] args) {
        AgilixManifestReader reader = new AgilixManifestReader();
        List<Course> courses = reader.parseManifest();
        for (Course course : courses)
            course.printCourse();
    }
}
